#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <poll.h>

#include <ez_net.h>
#include <ez_log.h>
#include <ez_event.h>
#include "cust_sign.h"

int sig_flag = 0;
char help[] = "help   - help \n"
              "close  - client active close client socket\n"
              "exit   - client exit.\n"
              "send   - client send 16 bytes data.\n"
              "recv   - client recv data.\n";

void signal_quit_handler(struct ez_signal *sig) { sig_flag = 1; }

int read_char_from_stdin(char *buf, size_t bufsize) {
    ssize_t nread;

    nread = read(STDIN_FILENO, buf, bufsize);

    if (nread > 0) {
        // read 中有个回车符换行符.
        buf[nread--] = '\0';
        while (nread >= 0 && (buf[nread] == '\n' || buf[nread] == '\r')) {
            buf[nread] = '\0';
            --nread;
        }
        return (int) nread;
    }
    return 0;
}

int aeWait(int fd, int mask, long long milliseconds) {
    struct pollfd pfd;
    int retmask = 0, retval;

    memset(&pfd, 0, sizeof(pfd));
    pfd.fd = fd;
    if (mask & AE_READABLE) pfd.events |= POLLIN;
    if (mask & AE_WRITABLE) pfd.events |= POLLOUT;

    if ((retval = poll(&pfd, 1, milliseconds)) == 1) {
        if (pfd.revents & POLLIN) retmask |= AE_READABLE;
        if (pfd.revents & POLLOUT) retmask |= AE_WRITABLE;
        if (pfd.revents & POLLERR) retmask |= AE_WRITABLE;
        if (pfd.revents & POLLHUP) retmask |= AE_WRITABLE;
        return retmask;
    } else {
        return retval;
    }
}

void wait_quit(int c) {
    char cmd[16];
    char err[256];
    char red[16];
    char buf[16];
    int r;
    ssize_t nwrite, nread;

    while (1) {
        write(STDOUT_FILENO, ":>", 2);

        int mask = aeWait(c, AE_READABLE, 1000);
        if (mask & AE_READABLE) {
            r = ez_net_read(c, red, sizeof(red), &nread);
            // 对端未关闭下，一直读取是[result=>ANET_OK,     nread => XX]
            // 对端未关闭下，一直读取是[result=>ANET_EAGAIN, nread => 0]
            log_hexdump(LOG_INFO, red, nread, "client %d > 接收数据 %li bytes, result: %d.", c, nread, r);

            if (r == ANET_CLOSE) {
                ez_net_close_socket(c);
                log_error("client %d > 接收数据 error [%d, %s]", c, r, err);
            }

            if (r == ANET_ERR) {
                memset(&err[0], 0, sizeof(err));
                r = ez_net_socket_error(c, &err[0], sizeof(err) - 1);
                log_error("client %d > 接收数据 error [%d, %s]", c, r, err);
            }
        }

        r = read_char_from_stdin(cmd, 16);

        if (sig_flag == 1)
            break;

        if (r <= 0) {
            continue;
        } else if (strcmp(cmd, "help") == 0) {
            write(STDOUT_FILENO, &help[0], sizeof(help) - 1);
        } else if (strcmp(cmd, "close") == 0) {
            log_info("client %d > 客户端主动关闭 ", c);
            ez_net_close_socket(c);
            c = 0;
        } else if (strcmp(cmd, "send") == 0) {
            // 向已经处于close_wait状态的socket发消息，是会出现：Signal: SIGPIPE (Broken pipe)
            r = ez_net_write(c, buf, sizeof(buf), &nwrite);
            log_info("client %d > 发送数据 %li bytes, result: %d .", c, nwrite, r);

            if (r == ANET_ERR) {
                memset(&err[0], 0, sizeof(err));
                r = ez_net_socket_error(c, &err[0], sizeof(err) - 1);
                log_error("client %d > 发送数据 error [%d, %s]", c, r, err);
            }
        } else if (strcmp(cmd, "recv") == 0) {
            r = ez_net_read(c, red, sizeof(red), &nread);
            // 对端未关闭下，一直读取是[result=>ANET_OK,     nread => XX]
            // 对端未关闭下，一直读取是[result=>ANET_EAGAIN, nread => 0]
            log_hexdump(LOG_INFO, red, nread, "client %d > 接收数据 %li bytes, result: %d.", c, nread, r);

            if (r == ANET_CLOSE) {
                ez_net_close_socket(c);
            }

            if (r == ANET_ERR) {
                memset(&err[0], 0, sizeof(err));
                r = ez_net_socket_error(c, &err[0], sizeof(err) - 1);
                log_error("client %d > 接收数据 error [%d, %s]", c, r, err);
            }
        } else if (strcmp(cmd, "exit") == 0) {
            ez_net_close_socket(c);
            break;
        }
    }
}

int main(int argc, char **argv) {
    char *svr_addr = "localhost";
    int svr_port = 9090;
    char socket_name[256];
    int socket_port;

    if (argc > 1) {
        svr_addr = argv[1];
    }

    cust_signal_init();

    log_init(LOG_DEBUG, NULL);

    int c = ez_net_tcp_connect(svr_addr, svr_port);
    if (c > 0) {
        ez_net_socket_name(c, socket_name, 255, &socket_port);
        socket_name[255] = '\0';
        log_info("client[id:%d %s:%d => %s:%d] connect success.", c, socket_name, socket_port, svr_addr, svr_port);

        ez_net_set_non_block(c);
        wait_quit(c);
        ez_net_close_socket(c);
    }

    log_release();
    return 0;
}
